﻿namespace FSharpChess

module Move_do =
    let PawnForOff (pr) = if pr.prIsWhite then 8 else -8
    let PawnAttRightC (cl) = if cl then 9 else -7
    let PawnAttLeftC (cl) = if cl then 7 else -9
    let PawnForOffC (cl) = if cl then 8 else -8
    let CanClaimMoveRepetitionDraw(nm,psn) = 
        if psn.Mhst.Count = 0 then
            false
        else
            let rec canclm i rc =
                if i<0 then
                    false
                else
                    let move = psn.Mhst.[i]
                    if (psn.Pcs.[move.mvPiece].Name = enmName.Pawn || move.pieceCaptured <> -1) then
                        false
                    elif (move.HashCodeA = psn.Hca && move.HashCodeB = psn.Hcb) then
                        if rc >= nm then
                            true
                        else
                            canclm (i-2) (rc+1)
                    else
                        canclm (i-2) rc
            canclm (psn.Mhst.Count - 1) 1       
    let CanClaimThreeMoveRepetitionDraw(psn) = CanClaimMoveRepetitionDraw(3,psn)
    let GetHashCodeA (pieceid,ordinal,pc) = 
        let a = (int)((uint32)(pieceid)<<<6) + ordinal
        if pc.HasBeenPromoted then
            let nm = pc.Name
            let ulongPromotionModifier = 
                if nm = enmName.Queen then 830859827498573475UL
                elif nm = enmName.Rook then 37500384876452947UL
                elif nm = enmName.Bishop then 448573857309865743UL
                elif nm = enmName.Knight then 294375032850265937UL
                else 0UL
            Ref.HashCodesA.[a] + ulongPromotionModifier
        else      
            Ref.HashCodesA.[a]
    let GetHashCodeB (pieceid,ordinal,pc) = 
        let a = (int)((uint32)(pieceid)<<<6) + ordinal
        if pc.HasBeenPromoted then
            let nm = pc.Name
            let ulongPromotionModifier = 
                if nm = enmName.Queen then 790423450762398573UL
                elif nm = enmName.Rook then 394756026094872034UL
                elif nm = enmName.Bishop then 629385632983478593UL
                elif nm = enmName.Knight then 283469276067858673UL
                else 0UL
            Ref.HashCodesB.[a] + ulongPromotionModifier
        else
            Ref.HashCodesB.[a]
    let Capture (pcid,psn) =
        let pc = psn.Pcs.[pcid]
        if pc.pcIsWhite then psn.Wct <- psn.Wct - 1 else psn.Bct <- psn.Bct - 1
        if pc.SquareOrd <> -1 then 
            psn.Sqs.[pc.SquareOrd] <- -1
        psn.Pcs.[pcid] <- {pc with SquareOrd= -1}
    let Uncapture(pcid,sqid,psn) =
        let pc = psn.Pcs.[pcid]
        if pc.pcIsWhite then psn.Wct <- psn.Wct + 1 else psn.Bct <- psn.Bct + 1
        psn.Pcs.[pcid] <- {pc with SquareOrd=sqid}
        psn.Sqs.[sqid] <- pcid
    //do some code to check whether move is consistent
    exception InconException of string
    let CheckCon(psn)=
        for i=0 to 31 do
            let sqid = psn.Pcs.[i].SquareOrd
            if sqid <> -1 && psn.Sqs.[sqid]<>i then
                raise (InconException("Piece not on square "+i.ToString()))
        for i=0 to 63 do
            let pcid=psn.Sqs.[i]
            if pcid<> -1 && psn.Pcs.[pcid].SquareOrd<>i then
                raise (InconException("Square does not have right piece "+i.ToString()))

    let DoMoveOK(mv,psn) =
        //do moves first
        let pc = psn.Pcs.[mv.mvPiece]
        let pccap = if mv.pieceCaptured <> -1 then psn.Pcs.[mv.pieceCaptured] else Piece.blank
        //do castling
        let pieceRookidOrd = 
            if mv.mvName = CastleKingSide && pc.pcIsWhite then
                Ref.GetOrdFR (7,0)
            elif mv.mvName = CastleKingSide then
                Ref.GetOrdFR (7,7)
            elif mv.mvName = CastleQueenSide && pc.pcIsWhite then
                Ref.GetOrdFR (0,0)
            elif mv.mvName = CastleQueenSide then
                Ref.GetOrdFR (0,7)
            else
                -1
        let pieceRookid = if pieceRookidOrd <> -1 then psn.Sqs.[pieceRookidOrd] else -1
        let newpieceRookidOrd =
            if mv.mvName = CastleKingSide then
                Ref.GetOrdFR(5, Ref.ri.[mv.To])
            elif mv.mvName = CastleQueenSide then
                Ref.GetOrdFR(3, Ref.ri.[mv.To])
            else
                -1
        let pieceRook = if pieceRookid <> -1 then psn.Pcs.[pieceRookid] else Piece.blank
        let newpieceRook = if pieceRookid <> -1 then {pieceRook with SquareOrd=newpieceRookidOrd} else Piece.blank
        //do promotion
        let newpc =
            match mv.mvName with
                |PawnPromotionBishop -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Bishop;HasBeenPromoted=true}
                |PawnPromotionKnight -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Knight;HasBeenPromoted=true}
                |PawnPromotionRook -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Rook;HasBeenPromoted=true}
                |PawnPromotionQueen -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Queen;HasBeenPromoted=true}
                |_ -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1}
        //do psn changes
        if mv.pieceCaptured <> -1 then
            Capture(mv.pieceCaptured,psn)
        psn.Sqs.[mv.From] <- -1
        psn.Sqs.[mv.To] <- mv.mvPiece
        psn.Pcs.[mv.mvPiece] <- newpc
        if pieceRookidOrd <> -1 then 
            psn.Sqs.[pieceRookidOrd] <- -1
            psn.Sqs.[newpieceRookidOrd] <- pieceRookid
            psn.Pcs.[pieceRookid] <- newpieceRook
        //check for invalidmove
        let IsInCheck = Attack.is_in_check(Attack.getPlayer(pc.pcIsWhite),psn,true)
        //Undo invalid moves
        if IsInCheck then
            psn.Sqs.[mv.From] <- mv.mvPiece
            psn.Sqs.[mv.To] <- -1
            if mv.pieceCaptured <> -1 then
                Uncapture(mv.pieceCaptured,pccap.SquareOrd,psn)
            psn.Pcs.[mv.mvPiece] <- pc
            if pieceRookidOrd <> -1 then 
                psn.Sqs.[pieceRookidOrd] <- pieceRookid
                psn.Sqs.[newpieceRookidOrd] <- -1
                psn.Pcs.[pieceRookid] <- pieceRook
            //check OK remove this when fine
            //CheckCon(psn)
            false
        else
            psn.Hca <- psn.Hca^^^GetHashCodeA(mv.mvPiece,mv.From,pc) // Un-XOR current piece position
            psn.Hcb <- psn.Hcb^^^GetHashCodeB(mv.mvPiece,mv.From,pc) // Un-XOR current piece position
            if mv.pieceCaptured <> -1 then
                psn.Hca <- psn.Hca^^^GetHashCodeA(mv.pieceCaptured,pccap.SquareOrd,pccap) // Un-XOR current piece position
                psn.Hcb <- psn.Hcb^^^GetHashCodeB(mv.pieceCaptured,pccap.SquareOrd,pccap) // Un-XOR current piece position
            psn.Trn<-psn.Trn+1
            psn.PlayerToPlay <- Attack.OtherPlayer(psn.PlayerToPlay)
            psn.Scr <- -1
            //do castling
            if mv.mvName = CastleKingSide||mv.mvName = CastleQueenSide then
                psn.Hca <- psn.Hca^^^GetHashCodeA(pieceRookid,pieceRookidOrd,pieceRook)
                psn.Hcb <- psn.Hcb^^^GetHashCodeB(pieceRookid,pieceRookidOrd,pieceRook)
                psn.Hca <- psn.Hca^^^GetHashCodeA(pieceRookid,newpieceRookidOrd,newpieceRook)
                psn.Hcb <- psn.Hcb^^^GetHashCodeB(pieceRookid,newpieceRookidOrd,newpieceRook)
                if pc.pcIsWhite then psn.Wcas <- true else psn.Bcas <- true
            psn.Hca <- psn.Hca^^^GetHashCodeA(mv.mvPiece,mv.To,newpc) 
            psn.Hcb <- psn.Hcb^^^GetHashCodeB(mv.mvPiece,mv.To,newpc) 
            mv.pcLastMoveTurnNo <- pc.LastMoveTurnNo
            if CanClaimThreeMoveRepetitionDraw(psn) then
                psn.Hca <- psn.Hca^^^31UL
                psn.Hcb <- psn.Hcb^^^29UL
                mv.IsThreeMoveRepetition <- true
            mv.HashCodeA <- psn.Hca
            mv.HashCodeB <- psn.Hcb
            //check OK remove this when fine
            //CheckCon(psn)
            psn.Mhst.Add(mv)
            true
    let Undo(move,psn) =
        psn.Hca <- move.OldHashCodeA
        psn.Hcb <- move.OldHashCodeB
        let pc = psn.Pcs.[move.mvPiece]
        psn.Sqs.[move.From] <- move.mvPiece// Set piece on board
        psn.Pcs.[move.mvPiece] <- {pc with SquareOrd=move.From;LastMoveTurnNo=move.pcLastMoveTurnNo;NoOfMoves=pc.NoOfMoves-1}
        psn.Sqs.[move.To] <- -1 // Blank square where moved to
        if (move.pieceCaptured <> -1) then
            if (move.mvName <> EnPassent) then
                Uncapture(move.pieceCaptured,move.To,psn)
            else
                Uncapture(move.pieceCaptured,move.To - PawnForOff(Attack.getPlayer(psn.Pcs.[move.mvPiece].pcIsWhite)),psn)
        //do castling
        if move.mvName = CastleKingSide then
            let pieceRookid =
                if psn.Pcs.[move.mvPiece].pcIsWhite then
                    psn.Sqs.[Ref.GetOrdFR (5,0)]
                else 
                    psn.Sqs.[Ref.GetOrdFR (5,7)]
            let pieceRook = psn.Pcs.[pieceRookid]
            psn.Pcs.[pieceRookid] <- {pieceRook with SquareOrd=Ref.GetOrdFR(7, Ref.ri.[pc.SquareOrd])}
            psn.Sqs.[Ref.GetOrdFR(7, Ref.ri.[pc.SquareOrd])] <- pieceRookid
            psn.Sqs.[Ref.GetOrdFR(5, Ref.ri.[pc.SquareOrd])] <- -1
            if pc.pcIsWhite then psn.Wcas <- false else psn.Bcas <- false
        if move.mvName = CastleQueenSide then
            let pieceRookid =
                if psn.Pcs.[move.mvPiece].pcIsWhite then
                    psn.Sqs.[Ref.GetOrdFR (3,0)]
                else 
                    psn.Sqs.[Ref.GetOrdFR (3,7)]
            let pieceRook = psn.Pcs.[pieceRookid]
            psn.Pcs.[pieceRookid] <- {pieceRook with SquareOrd=Ref.GetOrdFR(0, Ref.ri.[pc.SquareOrd])}
            psn.Sqs.[Ref.GetOrdFR(0, Ref.ri.[pc.SquareOrd])] <- pieceRookid
            psn.Sqs.[Ref.GetOrdFR(3, Ref.ri.[pc.SquareOrd])] <- -1
            if pc.pcIsWhite then psn.Wcas <- false else psn.Bcas <- false
        //do promotion
        if move.mvName = PawnPromotionQueen|| move.mvName = PawnPromotionRook||move.mvName = PawnPromotionBishop||move.mvName = PawnPromotionKnight then 
            psn.Pcs.[move.mvPiece] <- {pc with SquareOrd=move.From;LastMoveTurnNo=move.pcLastMoveTurnNo;NoOfMoves=pc.NoOfMoves-1;Name=enmName.Pawn;HasBeenPromoted=false}
        psn.Trn <- psn.Trn-1
        psn.PlayerToPlay <- Attack.OtherPlayer(psn.PlayerToPlay)
        psn.Scr <- move.OldScr
        psn.Mhst.RemoveAt(psn.Mhst.Count - 1)
        //check OK remove this when fine
        //CheckCon(psn)


